package cn.wizzer.app.crown.modules.services.impl.result;

import cn.wizzer.app.crown.commons.util.DateUtil;
import cn.wizzer.app.crown.modules.models.CrownConfig;
import cn.wizzer.app.crown.modules.models.CrownMatch;
import cn.wizzer.app.crown.modules.models.CrownSubResult;
import cn.wizzer.app.crown.modules.models.constant.CrownConstant;
import cn.wizzer.app.crown.modules.models.enums.MatchTypeEnum;
import cn.wizzer.app.crown.modules.models.enums.SubResultEnum;
import cn.wizzer.app.crown.modules.services.CrownConfigService;
import cn.wizzer.app.crown.modules.services.CrownMatchService;
import cn.wizzer.app.crown.modules.services.CrownSubResultService;
import cn.wizzer.app.crown.modules.services.result.FTMatchResultService;
import cn.wizzer.app.sys.modules.services.SysParamService;
import cn.wizzer.framework.base.Result;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONArray;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Dao;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@IocBean(args = {"refer:dao"})
@Service(interfaceClass = FTMatchResultService.class)
public class FTMatchResultServiceImpl extends BaseServiceImpl<CrownMatch> implements FTMatchResultService {

    public FTMatchResultServiceImpl(Dao dao) {
        super(dao);
    }

    private static final Log log = Logs.get();

    @Inject
    private RedisService redisService;
    @Inject
    private CrownMatchService matchService;
    @Inject
    private CrownSubResultService subResultService;
    @Inject
    @Reference(retries = 0, timeout = 500000)
    private CrownConfigService crownConfigService;
    @Inject
    @Reference
    private SysParamService sysParamService;

    private static final Pattern MATCH_COMPILE = Pattern.compile("gdata = Array\\((.*?)\\);");

    @Override
    public void crawler(CrownMatch match, CrownConfig crownConfig) {
        JSONArray jsonArray = this.getData(match, crownConfig);
        if (jsonArray == null) {
            this.sendEmail(match);
            return;
        }
        // ==================进球数===============
        this.goalsNum(match, jsonArray);
        // 更新赛果
        matchService.updateMatchResult(match, "task");
    }


    /**
     * 给管理员发送邮件
     *
     * @param match
     */
    private void sendEmail(CrownMatch match) {
        // 开赛时间(美东)转系统时间
        Date date = DateUtil.timeZoneConvert(match.getDateTime(), "America/New_York", TimeZone.getDefault().getID());
        if (null == date) {
            log.error("爬取赛果发送邮件失败,转换date为null,match:" + match);
            return;
        }
        long matchTime = date.getTime();
        long l = (System.currentTimeMillis() - matchTime) / 1000;
        // 超过赛事时间2小时 4小时 6小时发送邮件
        if ((l >= 3600 * 2 && l <= 3600 * 2.5) || (l >= 3600 * 4 && l <= 3600 * 4.5) || (l >= 3600 * 6 && l <= 3600 * 6.5)) {
            log.info("赛果爬取失败发送邮件。开赛时间已过：" + l + "s");
            sysParamService.sendEmail("3412995626@qq.com", "赛果获取不到",
                    "mid：" + match.getMid() + "。开赛时间已过：" + l + "s" + "。位置：hello");
        }
    }


    /**
     * 请求皇冠并检验数据
     *
     * @param match
     * @param crownConfig
     * @return
     */
    private JSONArray getData(CrownMatch match, CrownConfig crownConfig) {
        // 66.133.86.54/app/member/account/result/FT_result_new.php?uid=tia85gppm22923423l237718&gtype=FT&game_id=4089557&langx=en-us
        String tailUrl = "/app/member/account/result/FT_result_new.php?uid=" + crownConfig.getUid()
                + "&gtype=FT&game_id=" + match.getMid() + "&langx=" + CrownConstant.LANGX;
        String body = crownConfigService.getCrownData(tailUrl, crownConfig, CrownConstant.RE_TRY_TIMES);
//        String body = "var gdata = Array('4513843','2020-11-17','03:25p','欧洲U21青年锦','奥地利U21','安道尔U21','20784','20783','Y','false','-11','-11','不显示赛程','DL','不显示赛程','DL','-11','-11','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','取消','N/A','取消','N/A','-11','-11','不显示赛程','DL','取消','N/A','取消','N/A','不显示赛程','DL','-11','-11','不显示赛程','DL','不显示赛程','DL','-11','-11','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','-11','-11','不显示赛程','DL','不显示赛程','DL','取消','N/A','取消','N/A','-11','-11','不显示赛程','DL','不显示赛程','DL','-11','-11','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','取消','N/A','取消','N/A','不显示赛程','DL','不显示赛程','DL','取消','N/A','取消','N/A','不显示赛程','DL','取消','N/A','取消','N/A','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','取消','N/A','取消','N/A','不显示赛程','DL','不显示赛程','DL','不显示赛程','DL','取消','N/A','取消','N/A','','','');";
        // 检测账号被登出
        if (crownConfigService.checkLogout(body, crownConfig)) {
            return null;
        }
        // body是空，监测到皇冠维护都return
        if (Strings.isBlank(body)) {
            log.error("爬取赛果失败，body为空");
            return null;
        } else if (crownConfigService.checkMaintain(body, crownConfig)) {
            return null;
        }
        // 解析body
        Matcher m = MATCH_COMPILE.matcher(body);
        // 如果获取不到gdata= return
        if (!m.find()) {
            log.error("赛果数据中没有gdata=,mid:" + match.getMid());
            return null;
        }
        log.info("开始解析足球赛事赛果, mid:" + match.getMid());
        String group = m.group();
        JSONArray jsonArray = JSONArray.parseArray("[" + group.substring(14, group.length() - 2) + "]");
        // 如果赛事没有结束 return
        if ("N".equals(jsonArray.getString(8))) {
            log.error("赛果数据中结束状态为N,mid:" + match.getMid());
            return null;
        }
        // 校验jsonArray与match的主客队名是否相同
        // 处理match中主客队名后在进行对比
        String mbTeam = match.getMbTeam();
        String tgTeam = match.getTgTeam();
        if (mbTeam.contains("-")) {
            mbTeam = mbTeam.substring(0, mbTeam.indexOf("-")).trim();
        }
        if (tgTeam.contains("-")) {
            tgTeam = tgTeam.substring(0, tgTeam.indexOf("-")).trim();
        }
        if (!jsonArray.getString(4).trim().equals(mbTeam) || !jsonArray.getString(5).trim().equals(tgTeam)) {
            log.error("赛果数据中主客队名与数据库中主客队名不相同,mid:" + match.getMid());
            Result.breakWith("赛果数据中主客队名与数据库中主客队名不相同,mid:" + match.getMid());
            return null;
        }
        return jsonArray;
    }


    /**
     * key: 赛事异常信息
     * value: 赛事阶段标记 & 文字
     */
    private Map<Integer, String> tagMap = Result.createHashMap(
            12, "NO1&上半场开场 - 14:59 分钟",
            18, "NO2&15:00 – 29:59 分钟",
            30, "NO3&30:00分钟 - 半场",
            74, "NO4&半场",
            40, "NO5&下半场开场 - 59:59 分钟",
            46, "NO6&60:00 – 74:59 分钟",
            58, "NO7&75:00分钟 - 全场完场",
            68, "NO8&全场"
    );


    /**
     * 进球数爬取
     *
     * @param match     CrownMatch对象。目前对象中的主客队比分属性，在结算中有用到
     * @param jsonArray 爬取的数据源
     */
    @Aop(TransAop.REPEATABLE_READ)
    private void goalsNum(CrownMatch match, JSONArray jsonArray) {
        ArrayList<CrownSubResult> subResults = new ArrayList<>();
        String hInBall, cInBall, exInfo;
        // 赛事异常flag
        boolean matchExFlag = false;
        for (Map.Entry<Integer, String> entry : tagMap.entrySet()) {
            CrownSubResult subResult = new CrownSubResult();
            Integer key = entry.getKey();
            exInfo = jsonArray.getString(key);
            // 15分钟阶段赛果都为‘不显示赛程’，电竞比赛一般没有15分钟盘口，所以不入库。
            if ("不显示赛程".equals(exInfo) && key != 68 && key != 74) {
                continue;
            }
            hInBall = jsonArray.getString(key - 2);
            cInBall = jsonArray.getString(key - 1);
            String[] split = entry.getValue().split("&");
            // 如果异常信息不为空，说明赛事出现异常，取异常信息赋值给比分
            if ((Strings.isNotBlank(exInfo))) {
                hInBall = exInfo;
                cInBall = exInfo;
                // 赛事腰斩不算赛事异常
                if (exInfo.contains("腰斩")) {
                    match.setIsCut(true);
                } else {
                    matchExFlag = true;
                }
            }
            // 赛事表字段存全场、半场比分、赛事状态
            if ("NO4".equals(split[0])) {
                match.setMbInBallHf(hInBall);
                match.setTgInBallHf(cInBall);
                match.setIsHalfGone(true);
            } else if ("NO8".equals(split[0])) {
                match.setStage(matchExFlag ? CrownConstant.OTHER : CrownConstant.FINISHED);
                match.setMbInBall(hInBall);
                match.setTgInBall(cInBall);
            }
            subResult.setResultH(hInBall);
            subResult.setResultC(cInBall);
            subResult.setTag(split[0]);
            subResult.setRemark(split[1]);
            subResult.setMatchId(match.getId());
            subResult.setGameType(MatchTypeEnum.FOOTBALL);
            subResult.setType(SubResultEnum.GOALS_NUM);
            subResults.add(subResult);
        }
        // 处理赛果条目排序
        subResults.sort(Comparator.comparing(CrownSubResult::getTag));
        match.setSubResults(subResults);
    }
}
